// file: kernel/task/mutexlock.c
// autor: jiangxinpeng
// time: 2021.2.4
// copyright: (C) 2020-2050 by jiangxinpeng,All right are reserved.

#include <os/mutexlock.h>
#include <os/task.h>
#include <os/schedule.h>

static inline void MutexlockAddWait(task_t *task, mutexlock_t *mutexlock)
{
    // if lock is using and above time limit
    // add wait list,until other task unlock
    mutexlock->waiter++;
    list_add_tail(&task->list, &mutexlock->wait_list);
    TASK_INTO_WAITLIST(task);
    TaskBlock(TASK_BLOCKED);
}

static inline void MutexlockDelWait(task_t *task, mutexlock_t *mutexlock)
{
    mutexlock->waiter--;
    list_del_init(&task->list);
    TaskWakeUp(task);
}

// block type lock
int MutexlockLock(mutexlock_t *mutexlock, uint32_t mode)
{
    int count = 0;

    switch (mode)
    {
    case MUTEX_LOCK_MODE_BLOCK:

        // try get mutexlock successful
        if (MutexlockTryLock(mutexlock))
        {
            // if lock is using or lock faild
            MutexlockAddWait(cur_task, mutexlock);
        }
        break;
    case MUTEX_LOCK_MODE_WAITTING:
        do
        {
            // try get mutexlock successful
            if (MutexlockTryLock(mutexlock))
                count++;
            else
            {
                return 0;
            }

        } while (count < 10000);
        // get out try count,add waitting list and task into sleeping
        MutexlockAddWait(cur_task, mutexlock);
        break;
    case MUTEX_LOCK_MODE_ERROR:
        // check whether other task is using lock
        if (!MutexlockIsLock(mutexlock))
        {
            // try get mutexlock successful
            if (MutexlockTryLock(mutexlock))
            {
                return 0;
            }
        }
        return -1;

    default:
        break;
    }
}

void MutexlockUnlock(mutexlock_t *mutexlock)
{
    if (mutexlock != NULL && AtomicGet(&mutexlock->wait_lock))
    {
        // wait list empty
        if (mutexlock->waiter < 1)
        {
            // unlock mutexlock
            AtomicSet(&mutexlock->wait_lock, 0);
            return;
        }
        else
        {
            // wakeup a task and alloc mutexlock
            // get task from wait list
            task_t *task = list_first_owner(&mutexlock->wait_list, task_t, list);
            // task present
            if (task != NULL)
            {
                // del task from wait list
                // MutexlockDelWait(task, mutexlock);
            }
        }
    }
}
